#include "StdAfx.h"
#include "FnPatchGenerator.h"
#include "FnSurfaceGlobalParam.h"
#include <queue>
#include <set>

FnPatchGenerator::FnPatchGenerator(Mesh& _mesh)
: mesh_(_mesh), fnParam_(_mesh)
{
	mesh().add_property(fprop_cube_face_, "fprop_cube_face");
	mesh().add_property(eprop_cube_edge_flag_, "eprop_cube_edge_flag");
	mesh().add_property(vprop_cube_face_, "vprop_cube_face");

}

FnPatchGenerator::~FnPatchGenerator(void)
{
	mesh().remove_property(fprop_cube_face_);
	mesh().remove_property(eprop_cube_edge_flag_);
	mesh().remove_property(vprop_cube_face_);
}

bool FnPatchGenerator::get_shortest_path(const Mesh::VertexHandle _start_vh, const Mesh::VertexHandle _end_vh, std::vector<Mesh::HalfedgeHandle> &path)
{
	OpenMesh::EPropHandleT<float> EPropLength;
    OpenMesh::VPropHandleT<Mesh::HalfedgeHandle> VPropHHandle;
	OpenMesh::VPropHandleT<float> VPropPriority;
	OpenMesh::VPropHandleT<bool> VPropAuxFlag;
	OpenMesh::VPropHandleT<bool> VPropInQueue;

	
	 mesh().add_property(VPropPriority);
	 mesh().add_property(EPropLength);
	 mesh().add_property(VPropHHandle);
	 mesh().add_property(VPropAuxFlag);
	 mesh().add_property(VPropInQueue);

	 Mesh::EdgeIter e_it;
	 Mesh::VertexIter	  v_it;
     Mesh::Point           pnt[2];
	 float max_len = 0.0f, len;

	 std::priority_queue<VertexPriority> pq;

	 for(e_it=mesh().edges_begin(); e_it != mesh().edges_end(); ++e_it){
	    pnt[0] = mesh().point( mesh().to_vertex_handle(mesh().halfedge_handle(e_it, 0)));
        pnt[1] = mesh().point( mesh().to_vertex_handle(mesh().halfedge_handle(e_it, 1)));

		len = (pnt[0]-pnt[1]).length();

		mesh().property(EPropLength, e_it) = len;

		max_len += len;
	 }

	 for(v_it=mesh().vertices_begin(); v_it != mesh().vertices_end(); ++v_it){
		 mesh().property(VPropPriority, v_it) = max_len;
		 mesh().property(VPropAuxFlag, v_it) = false;
		 mesh().property(VPropInQueue, v_it) = false;
	 }

	 mesh().property(VPropPriority, _start_vh) = 0.0f;

	 VertexPriority pr;
	 pr.priority = 0.0f;
	 pr.vh_ = _start_vh;

	 Mesh::VertexHandle _vh, _nvh;

	 mesh().property(VPropInQueue, _start_vh) = true;

	   //CString str;
		

	 pq.push(pr);

	   //str.Format(_T("push [%d] : %f\n"), _start_vh.idx(), property(VPropPriority, _start_vh));

		//OutputDebugString(str);

	 while(!pq.empty()){

		do{
	       pr = pq.top();
		   pq.pop();
		   _vh = pr.vh_;

		  //str.Format(_T("pop [%d] : %f\n"), _vh.idx(), property(VPropPriority, _vh));

		//OutputDebugString(str);

		}while(mesh().property(VPropInQueue, _vh) == false);

		mesh().property(VPropInQueue, _vh) = false;

		mesh().property(VPropAuxFlag, _vh) = true;

		if(_vh == _end_vh)
			break;


			Mesh::VertexOHalfedgeIter vohe_it;

			for(vohe_it=mesh().voh_iter(_vh); vohe_it; ++vohe_it){
				_nvh = mesh().to_vertex_handle(vohe_it);

				len = mesh().property(EPropLength, mesh().edge_handle(vohe_it));

				if(mesh().property(VPropAuxFlag, _nvh))
					continue;

				if(mesh().property(eprop_cube_edge_flag_, mesh().edge_handle(vohe_it)) == true)
					continue;

				if(mesh().property(fprop_cube_face_, mesh().face_handle(vohe_it)) != kNone && mesh().property(fprop_cube_face_, mesh().face_handle(mesh().opposite_halfedge_handle(vohe_it))) != kNone)
					continue;

				if(mesh().property(VPropInQueue, _nvh) && mesh().property(VPropPriority, _nvh) > mesh().property(VPropPriority, _vh) + len){
					mesh().property(VPropInQueue, _nvh) = false;
				}

				if( !mesh().property(VPropInQueue, _nvh) ){
					mesh().property(VPropPriority, _nvh) = mesh().property(VPropPriority, _vh) + len;
					mesh().property(VPropHHandle, _nvh) = vohe_it;

					mesh().property(VPropInQueue, _nvh) = true;

					pr.priority = mesh().property(VPropPriority, _vh) + len;
					pr.vh_ = _nvh;
					pq.push(pr);

					//str.Format(_T("push [%d] : %f\n"), _nvh.idx(), property(VPropPriority, _nvh));

					//OutputDebugString(str);
				}
			}
	 }

	 path.clear();

	_vh = _end_vh;
	Mesh::HalfedgeHandle _he;
	bool is_path_valid = true;

	do{
		_he = mesh().property(VPropHHandle, _vh);

		if(_he.is_valid() == false){
			is_path_valid = false;
			path.clear();
			break;
		}

		_vh = mesh().from_vertex_handle(_he);
		path.insert(path.begin(), _he);
		mesh().property(eprop_cube_edge_flag_, mesh().edge_handle(_he)) = true;

	}while(_vh != _start_vh);

 	 mesh().remove_property(VPropPriority);
	 mesh().remove_property(EPropLength);
	 mesh().remove_property(VPropHHandle);
	 mesh().remove_property(VPropAuxFlag);
	 mesh().remove_property(VPropInQueue);

	return is_path_valid;
}

bool FnPatchGenerator::export_tag(const std::string &strFilename) 
 {
	 FILE    *fp2;

	 char str[100];

	 sprintf(str, "%s", strFilename.c_str());

	 fp2 = fopen(str, "w");

	 if(fp2 == NULL)
		 return false;

	 
	 fprintf(fp2, "%d\n", mesh().n_faces());
	 Mesh::FaceIter f_it;
	 std::vector<std::vector<Mesh::FaceHandle>> cube_map;
	 cube_map.resize(nSides);

	 for(f_it=mesh().faces_begin(); f_it!=mesh().faces_end(); ++f_it){
		 cube_map[(int)(mesh().property(fprop_cube_face_, f_it))].push_back(f_it);
		 
		 fprintf(fp2, "%d\n", (int)(mesh().property(fprop_cube_face_, f_it)));
	 }

	 fclose(fp2);

	 return true;
 }

void FnPatchGenerator::map_onto_cube(std::vector<Mesh::VertexHandle> &selected_vertices)
{
	int n = selected_vertices.size();
	std::vector<std::vector<Mesh::HalfedgeHandle>> cube_edge_path;
	cube_edge_path.resize(2*n-4);
	std::vector<std::vector<Mesh::HalfedgeHandle>> cube_edge_path_reverse;
	cube_edge_path_reverse.resize(2*n-4);
	int i;
	Mesh::EdgeIter e_it;
	Mesh::FaceIter f_it;

	// corner_vertices_.clear();
	corner_vertices_.resize(selected_vertices.size());

	for(i = 0; i < selected_vertices.size(); i++){
		corner_vertices_[i] = selected_vertices[i];
	}

	for(f_it = mesh().faces_begin(); f_it != mesh().faces_end(); ++f_it){
		mesh().property(fprop_cube_face_, f_it) = kNone;
	}

	for(e_it=mesh().edges_begin(); e_it!=mesh().edges_end(); ++e_it){
		mesh().property(eprop_cube_edge_flag_, e_it) = false;
	}

	//horizontal paths
	for(i = 0; i < n/4; i++)
	{
		int s = i*4;
		for (int j = 0; j < 4; j++)
		{
			get_shortest_path(selected_vertices[s+j], selected_vertices[s + (j + 1)%4], cube_edge_path[s+j]);
		}
	}

	int numedge = n;

	for(i = 0; i < n; i++)
	{
		for(int k = cube_edge_path[i].size()-1; k >= 0; k--)
			cube_edge_path_reverse[i].push_back(mesh().opposite_halfedge_handle(cube_edge_path[i][k]));
	}

	//vertical paths
	for(i = 0; i < n/4 - 1; i++)
	{
		int s = i*4;
		for (int j = 0; j < 4; j++)
		{
			get_shortest_path(selected_vertices[s+j], selected_vertices[s + j + 4], cube_edge_path[++numedge-1]);
		}
	}
	for(i = n; i < numedge; i++)
	{
		for(int k = cube_edge_path[i].size()-1;k >=0; k--)
			cube_edge_path_reverse[i].push_back(mesh().opposite_halfedge_handle(cube_edge_path[i][k]));
	}

	//top and bottom
	map_onto_cube_face(cube_edge_path[0], cube_edge_path[1], cube_edge_path[2], cube_edge_path[3], kPY); 
	map_onto_cube_face(cube_edge_path_reverse[n-1], cube_edge_path_reverse[n-2], cube_edge_path_reverse[n-3], cube_edge_path_reverse[n-4], kNY);

	//sides
	int id = 1;
	for(i = 0; i < n/4 - 1; i++)
	{
		int s = i*4;
		int s1 = (i+1)*4;
		for(int j=0; j < 4; j++)		//4 sides
		{
			map_onto_cube_face(cube_edge_path[s1+j], cube_edge_path_reverse[n+s+(j+1)%4], cube_edge_path_reverse[s+j], cube_edge_path[n+s+j],
				++id);
		}
	}
	printf("%d\n", id);
}

void FnPatchGenerator::map_onto_cube_face(std::vector<Mesh::HalfedgeHandle> &edge_path0, std::vector<Mesh::HalfedgeHandle> &edge_path1, std::vector<Mesh::HalfedgeHandle> &edge_path2, std::vector<Mesh::HalfedgeHandle> &edge_path3, int _whichface)
{
	OpenMesh::FPropHandleT<float> FPropAuxFlag;

	std::vector<Mesh::HalfedgeHandle> *edge_paths[4];
	std::queue<Mesh::FaceHandle> pq;

	edge_paths[0] = &edge_path0;
	edge_paths[1] = &edge_path1;
	edge_paths[2] = &edge_path2;
	edge_paths[3] = &edge_path3;

	mesh().add_property(FPropAuxFlag);

	Mesh::FaceIter f_it;
	Mesh::FaceHandle fh;

	for(f_it=mesh().faces_begin(); f_it!=mesh().faces_end(); ++f_it){
		mesh().property(FPropAuxFlag, f_it) = false;
	}

	for(int i = 0; i < 4; i++){
		for(int k = 0; k < edge_paths[i]->size(); k++){
			fh = mesh().face_handle(edge_paths[i]->at(k));


			if(mesh().property(FPropAuxFlag, fh) == false){
				pq.push(fh);
				mesh().property(FPropAuxFlag, fh) = true;
				mesh().property(fprop_cube_face_, fh) = _whichface;
			}

			mesh().property(FPropAuxFlag, mesh().face_handle(mesh().opposite_halfedge_handle(edge_paths[i]->at(k)))) = true;
		}
	}

	while(pq.empty()==false){
		fh = pq.front();
		pq.pop();	

		for(Mesh::FaceHalfedgeIter fh_it=mesh().fh_iter(fh); fh_it; ++fh_it){
			Mesh::FaceHandle fh2 = mesh().face_handle(mesh().opposite_halfedge_handle(fh_it));
			if(mesh().property(FPropAuxFlag, fh2))
				continue;

			pq.push(fh2);
			mesh().property(FPropAuxFlag, fh2) = true;
			mesh().property(fprop_cube_face_, fh2) = _whichface;
		}
	}

	mesh().remove_property(FPropAuxFlag);
}

void FnPatchGenerator::set_face_side_info()
{
	Mesh::FaceIter f_it;

	OpenMesh::FPropHandleT<int> fprop_side_idx;
	OpenMesh::VPropHandleT<VertexType> vprop_type;
	
	mesh().get_property_handle(fprop_side_idx, "fprop_side_idx");
	mesh().get_property_handle(vprop_type, "vprop_type");

	for(f_it = mesh().faces_begin(); f_it != mesh().faces_end(); ++f_it){
		mesh().property(fprop_side_idx, f_it) = mesh().property(fprop_cube_face_, f_it);
	}

 /*	Mesh::VertexIter      v_it;
	Mesh::VertexFaceIter  vf_it;
	int                   face_side_idx;
  //Param                 uvw;

 std::cout << "corner vertex infos" << std::endl;
  for (v_it=mesh().vertices_begin(); v_it!=mesh().vertices_end(); ++v_it)
  {
    std::set<int>   adj_face_side_idx;
    for (vf_it=mesh().vf_iter(v_it); vf_it; ++vf_it)
    {
      face_side_idx = mesh().property(fprop_side_idx, vf_it);
      if (face_side_idx < 0)
        continue;
      adj_face_side_idx.insert(face_side_idx);
    }

    if (adj_face_side_idx.size() != 3)
      continue;

      mesh().property(vprop_type, v_it) = kCornerVertex;
  
  }*/
}

void FnPatchGenerator::read_face_side_info(const std::string& _filename)
{
	/*
	//FnSurfaceGlobalParam fnParam(mesh());

	fnParam_.read_face_side_info(_filename);

	set_cube_face();

	Mesh::VertexIter v_it;
	Mesh::VertexFaceIter vf_it;
	CubeFaceType cube_face;

	corner_vertices_.clear();
	corner_vertices_.resize(8);

  for (v_it=mesh().vertices_begin(); v_it!=mesh().vertices_end(); ++v_it)
  {
    std::set<CubeFaceType>   adj_face_side_idx;
    for (vf_it=mesh().vf_iter(v_it); vf_it; ++vf_it)
    {
      cube_face = mesh().property(fprop_cube_face_, vf_it);
      if (cube_face < 0)
        continue;
      adj_face_side_idx.insert(cube_face);
    }

    if (adj_face_side_idx.size() != 3)
      continue;

    if (adj_face_side_idx.find(kNX) != adj_face_side_idx.end() &&          // -x
        adj_face_side_idx.find(kNY) != adj_face_side_idx.end() &&          // -y
        adj_face_side_idx.find(kNZ) != adj_face_side_idx.end())            // -z
      corner_vertices_[7] = v_it;
    else if (adj_face_side_idx.find(kPX) != adj_face_side_idx.end() &&     // +x
             adj_face_side_idx.find(kNY) != adj_face_side_idx.end() &&     // -y
             adj_face_side_idx.find(kNZ) != adj_face_side_idx.end())       // -z
      corner_vertices_[6] = v_it;
    else if (adj_face_side_idx.find(kPX) != adj_face_side_idx.end() &&     // +x
             adj_face_side_idx.find(kPY) != adj_face_side_idx.end() &&     // +y
             adj_face_side_idx.find(kNZ) != adj_face_side_idx.end())       // -z
      corner_vertices_[2] = v_it;
    else if (adj_face_side_idx.find(kNX) != adj_face_side_idx.end() &&     // -x
             adj_face_side_idx.find(kPY) != adj_face_side_idx.end() &&     // +y
             adj_face_side_idx.find(kNZ) != adj_face_side_idx.end())       // -z
      corner_vertices_[3] = v_it;
    else if (adj_face_side_idx.find(kNX) != adj_face_side_idx.end() &&     // -x
             adj_face_side_idx.find(kNY) != adj_face_side_idx.end() &&     // -y
             adj_face_side_idx.find(kPZ) != adj_face_side_idx.end())       // +z
      corner_vertices_[4] = v_it;
    else if (adj_face_side_idx.find(kPX) != adj_face_side_idx.end() &&     // +x
             adj_face_side_idx.find(kNY) != adj_face_side_idx.end() &&     // -y
             adj_face_side_idx.find(kPZ) != adj_face_side_idx.end())       // +z
      corner_vertices_[5] = v_it;
    else if (adj_face_side_idx.find(kPX) != adj_face_side_idx.end() &&     // +x
             adj_face_side_idx.find(kPY) != adj_face_side_idx.end() &&     // +y
             adj_face_side_idx.find(kPZ) != adj_face_side_idx.end())       // +z
      corner_vertices_[1] = v_it;
    else if (adj_face_side_idx.find(kNX) != adj_face_side_idx.end() &&     // -x
             adj_face_side_idx.find(kPY) != adj_face_side_idx.end() &&     // +y
             adj_face_side_idx.find(kPZ) != adj_face_side_idx.end())       // +z
      corner_vertices_[0] = v_it;
  }
  */
}

void FnPatchGenerator::init_parameterization()
{
	set_face_side_info();

	fnParam_.setNSides(this->nSides);
	fnParam_.set_weight_type(kUniform);
	fnParam_.init();  
}

void FnPatchGenerator::parameterization()
{
	//FnSurfaceGlobalParam fnParam(mesh());

	fnParam_.calc();
	fnParam_.update_patches();

	set_cube_face();

}

void FnPatchGenerator::update_patches()
{
	fnParam_.update_patch_paths();

	set_cube_face();

}

void FnPatchGenerator::set_cube_face()
{
	Mesh::FaceIter f_it;

	OpenMesh::FPropHandleT<int> fprop_side_idx;
	
	mesh().get_property_handle(fprop_side_idx, "fprop_side_idx");
	
	for(f_it = mesh().faces_begin(); f_it != mesh().faces_end(); ++f_it){
		mesh().property(fprop_cube_face_, f_it) = (CubeFaceType) mesh().property(fprop_side_idx, f_it);
	}

	OpenMesh::VPropHandleT<int> vprop_side_idx;

	mesh().get_property_handle(vprop_side_idx, "vprop_side_idx");

	Mesh::VertexIter v_it;

	for(v_it = mesh().vertices_begin(); v_it != mesh().vertices_end(); ++v_it)
	{
		int idx = mesh().property(vprop_side_idx, v_it);
		mesh().property(vprop_cube_face_, v_it) = (CubeFaceType) mesh().property(vprop_side_idx, v_it);
	}

}